package cn.com.bluemoon.daps.model.entity;

import cn.com.bluemoon.daps.common.domain.BaseModel;
import cn.com.bluemoon.daps.common.enums.DatabaseType;
import cn.com.bluemoon.daps.common.sql.IDbDataSource;
import cn.com.bluemoon.daps.common.validation.illegal.IllegalChar;
import cn.com.bluemoon.daps.model.dto.DapModelVersionInter;
import cn.com.bluemoon.daps.model.dto.DevImportCheck;
import com.alibaba.druid.DbType;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Objects;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Optional;

import static cn.com.bluemoon.daps.common.constant.DapConstant.DapModel.HIVE_COMPLEX_TYPES;

/**
 * <p>
 * 数据模型-模型中设计表-字段的表
 * </p>
 *
 * @author admin
 * @since 2020-08-03
 */
@Data
@Accessors
@ApiModel(value = "DapDataModelTableField", description = "数据模型-模型中设计表-字段的表")
public class DapDataModelTableField extends BaseModel<DapDataModelTableField>
        implements DapModelVersionInter, IDbDataSource {

    private static final long serialVersionUID = 1L;
    public static final String[] includeFields = new String[]{"autoIncrDefaultValue", "autoIncrement", "comment", "dataType", "defaultValue", "fieldName", "length", "decimalLength", "notNull", "primaryKey",
//            "fieldRank",
            "afterFieldName",
            "compoundType", "unSigned", "zeroFill", "tableName"};

    public static final LinkedHashMap<String, String> includeFieldAndDescMap = new LinkedHashMap<String, String>(){{
        put("autoIncrDefaultValue","自增初始值");
        put("autoIncrement","自增");
        put("comment","字段中文名");
        put("dataType","数据类型");
        put("defaultValue","缺省");
        put("fieldName","字段名");
        put("length","长度");
        put("decimalLength","小数位");
        put("notNull","非空");
        put("primaryKey","主键");
        put("fieldRank","序号");
        put("afterFieldName", "前驱字段名");
        put("compoundType","复合类型");
        put("unSigned","无符号");
        put("zeroFill","填充零");
        put("tableName","表英文名");
    }};

    @ApiModelProperty(value = "初始值,针对自增字段来说的初始值")
    @TableField(updateStrategy = FieldStrategy.IGNORED)
    private Long autoIncrDefaultValue;

    @ApiModelProperty(value = "是否为自增 0/null 为否， 1为是")
    private Integer autoIncrement;

    @ApiModelProperty(value = "业务描述")
    @Size(max = 1024, message = "业务描述不可以大于{max}")
    private String businessDesc;

    @ApiModelProperty(value = "字段注释，必填")
    @NotNull(message = "字段中文名不允许为空")
    @Size(max = 1024, message = "字段中文名不可以大于{max}")
    @NotNull(message = "字段中文名不允许为空" , groups = DevImportCheck.class)
    @Size(max = 1024, message = "字段中文名不可以大于{max}", groups = DevImportCheck.class)
    private String comment;

    @ApiModelProperty(value = "关联模型id")
    private String dataModelId;

    @ApiModelProperty(value = "数据类型")
    @NotNull(message = "数据类型不允许为空")
    @Size(max = 64, message = "数据类型不可以大于{max}")
    @NotNull(message = "数据类型不允许为空", groups = DevImportCheck.class)
    @Size(max = 64, message = "数据类型不可以大于{max}", groups = DevImportCheck.class)
    private String dataType;

    @ApiModelProperty(value = "缺省值，针对全字段来说，有别于初始值，默认为null")
    @TableField(updateStrategy = FieldStrategy.IGNORED)
    private String defaultValue;

    @ApiModelProperty(value = "字段名称")
    @NotNull(message = "字段名不允许为空")
    @Size(max = 64, message = "字段名称不可以大于{max}")
    @IllegalChar(name = "fieldName", message = "字段名中存在非法字符")
    // @NoSqlKeyword(name = "fieldName", message = "字段名不可为关键字")
    @NotNull(message = "字段名不允许为空", groups = DevImportCheck.class)
    @Size(max = 64, message = "字段名称不可以大于{max}", groups = DevImportCheck.class)
    private String fieldName;

    /** 旧字段 记录*/
    @TableField(exist = false)
    private String oldFieldName;

    @ApiModelProperty(value = "数据长度,数据类型括号里面的内容，包括字符长度、浮点精度/标度、日期精度")
    @Size(max = 1024, message = "数据长度不可以大于{max}")
    @TableField(updateStrategy = FieldStrategy.IGNORED)
    private String length;

    @ApiModelProperty(value = "小数位的长度")
    @TableField(updateStrategy = FieldStrategy.IGNORED)
    private Integer decimalLength;

    @ApiModelProperty(value = "数据模型-模型表id")
    @NotNull(message = "模型表id不允许为空")
    private String modelTableId;

    @ApiModelProperty(value = "是否为非空 0/null 为否 1为是")
    private Integer notNull;

    @ApiModelProperty(value = "是否为主键 0/null 为否， 1为是")
    private Integer primaryKey;

    @ApiModelProperty(value = "排序或者说字段排名")
    private Integer fieldRank;

    @ApiModelProperty(value = "字段的前一个字段名称", hidden = true)
    @TableField(exist = false)
    private String afterFieldName;

    @ApiModelProperty("复合类型定义")
    @Size(max = 2048, message = "复合类型定义不可以大于{max}")
    private String compoundType;

    /**
     * {@link #()}
     * 非{@link #getId()}和{@link #getId()}
     */
    @ApiModelProperty(value = "数据标准id关联，没关联默认为'0'")
    private String standardId = "0";

    @ApiModelProperty(value = "有无符号 0/null 为否， 1为是")
    private Integer unSigned;

    @ApiModelProperty(value = "如果是数值类型，是否自动填充0")
    private Integer zeroFill;

    @ApiModelProperty(value = "已发布版本的数据，记录模型下最新的唯一id，比如：最新表id,最新索引id ")
    private String pId;

    @ApiModelProperty(value = "标记当前数据是否为模型最新数据，null/0 为已发布数据、1为该模型下的最新数据")
    private Integer dataNewest;

    @ApiModelProperty(value = "标记当前数据是否为模型最新数据，null/0 为已发布数据、1为该模型下的最新数据")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime ddlChangeTime;

    @ApiModelProperty(value = "未引用标准设为null,在字段表中引用设为1表示人工引用，2表示自动引用")
    private Integer standardMethod;

    /**
     * 用于查询字段+表名的对象
     */
    @ApiModelProperty(value = "表英文名称", hidden = true)
    @TableField(exist = false)
    private String tableName;

    @ApiModelProperty("排序的规则, 0:asc 1:desc")
    @TableField(exist = false)
    private Integer sorting;

    @ApiModelProperty(value = "用于数据对比中多版本的关联键值", hidden = true)
    @TableField(exist = false)
    private String bmKey;

    @ApiModelProperty(value = "数据库类型，用于区分不同数据库，equals中mysql忽略大小写判断相等", hidden = true)
    @TableField(exist = false)
    private DatabaseType dbType;

    public DapDataModelTableField() {
        this.dbType = DatabaseType.MYSQL;
    }

    public DapDataModelTableField(DatabaseType dbType) {
        this.dbType = dbType;
    }

    @Override
    @JsonIgnore
    public Integer getRank() {
        return getFieldRank();
    }

    /**
     * 提取、获取字段类型+复合内容
     *
     * @param dataTypeAndComplexStr 类型+复合内容
     * @return 获取字段类型+复合内容
     */
    @JsonIgnore
    public static Tuple2<String, Optional<String>> getHiveStdDataTypeAndComplexTypeTuples(String dataTypeAndComplexStr) {
        if (dataTypeAndComplexStr == null) {
            return null;
        }
        String fieldSourceDataType = dataTypeAndComplexStr.trim();
        Optional<String> compoundDataTypeOpt = HIVE_COMPLEX_TYPES.keySet().stream().map(k -> {
            if (fieldSourceDataType.toLowerCase().startsWith(k)) {
                return k;
            }
            return null;
        }).filter(java.util.Objects::nonNull).findFirst();
        if (compoundDataTypeOpt.isPresent()) {
            String dataType = compoundDataTypeOpt.get();
            String stdDataType = HIVE_COMPLEX_TYPES.getOrDefault(dataType, dataType);
            String compoundType = fieldSourceDataType.substring(dataType.length());
            return Tuples.of(stdDataType, Optional.of(compoundType));
        } else {
            String stdDataType = fieldSourceDataType;
            return Tuples.of(stdDataType, Optional.empty());
        }
    }

    /**
     * 组合两个字段，复合类型数值
     *
     * @param dataType 数据类型
     * @param complex 复合类型内容
     * @return 复合内容
     */
    public static Optional<String> combineDataTypeAndComplexStr(String dataType, String complex){
        if (dataType == null) {
            return Optional.empty();
        }
        return Optional.of(StringUtils.isNotBlank(complex) ? dataType : dataType + complex);
    }


    public boolean equals(Object o, DatabaseType databaseType) {
        //noinspection AliControlFlowStatementWithoutBraces
        if (this == o) {
            return true;
        }
        //noinspection AliControlFlowStatementWithoutBraces
        if (!(o instanceof DapDataModelTableField)) {
            return false;
        }
        //noinspection AliControlFlowStatementWithoutBraces
      /*  if (!super.equals(o)) {
            return false;
        }*/
        DapDataModelTableField that = (DapDataModelTableField) o;
        return Objects.equal(getId(), that.getPId()) &&
                (!databaseType.equals(DatabaseType.MYSQL) || Objects.equal(comment, that.comment))
                && Objects.equal(dataType, that.dataType) &&
                Objects.equal(length, that.length) &&
                Objects.equal(decimalLength, that.decimalLength) &&
                Objects.equal(compoundType, that.compoundType);
    }

    public boolean equals(String length, Integer decimalLength) {

        return
                Objects.equal(length, length) &&
                Objects.equal(decimalLength, decimalLength);

    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DapDataModelTableField field = (DapDataModelTableField) o;
        if (DatabaseType.MYSQL.equals(dbType)) {
//            Objects.equal(autoIncrDefaultValue, field.autoIncrDefaultValue) 忽略自增初始值的对比
            return Objects.equal(autoIncrement, field.autoIncrement) &&
                    Objects.equal(comment, field.comment) &&
                    StringUtils.equalsIgnoreCase(dataType, field.dataType) &&
                    StringUtils.equalsIgnoreCase(defaultValue, field.defaultValue) &&
                    StringUtils.equalsIgnoreCase(fieldName, field.fieldName) &&
                    StringUtils.equalsIgnoreCase(length, field.length) &&
                    Objects.equal(decimalLength, field.decimalLength) &&
                    Objects.equal(notNull, field.notNull) &&
                    Objects.equal(primaryKey, field.primaryKey) &&
//                    Objects.equal(fieldRank, field.fieldRank) &&
                    Objects.equal(afterFieldName, field.afterFieldName) &&
                    StringUtils.equalsIgnoreCase(compoundType, field.compoundType) &&
                    Objects.equal(unSigned, field.unSigned) &&
                    Objects.equal(zeroFill, field.zeroFill) &&
                    StringUtils.equalsIgnoreCase(tableName, field.tableName);
        }else if(DatabaseType.PGSQL.equals(dbType)){
            return
                    StringUtils.equalsIgnoreCase(dataType, field.dataType) &&
                    StringUtils.equalsIgnoreCase(length, field.length) &&
                    Objects.equal(decimalLength, field.decimalLength);
        }
        return Objects.equal(autoIncrDefaultValue, field.autoIncrDefaultValue) &&
                Objects.equal(autoIncrement, field.autoIncrement) &&
                Objects.equal(comment, field.comment) &&
                Objects.equal(dataType, field.dataType) &&
                Objects.equal(defaultValue, field.defaultValue) &&
                Objects.equal(fieldName, field.fieldName) &&
                Objects.equal(length, field.length) &&
                Objects.equal(decimalLength, field.decimalLength) &&
                Objects.equal(notNull, field.notNull) &&
                Objects.equal(primaryKey, field.primaryKey) &&
                Objects.equal(fieldRank, field.fieldRank) &&
                Objects.equal(compoundType, field.compoundType) &&
                Objects.equal(unSigned, field.unSigned) &&
                Objects.equal(zeroFill, field.zeroFill) &&
                Objects.equal(tableName, field.tableName);
    }

    public boolean equals(Object o, DbType dbType) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof DapDataModelTableField)) {
            return false;
        }
        DapDataModelTableField that = (DapDataModelTableField) o;
        if(dbType.equals(DbType.hive)){
            return
                    StringUtils.equalsIgnoreCase(dataType, that.dataType) &&
                    StringUtils.equalsIgnoreCase(length, that.length) &&
                    Objects.equal(decimalLength, that.decimalLength) &&
                    Objects.equal(fieldName, that.fieldName) &&
                    Objects.equal(compoundType, that.compoundType);
        }else if(dbType.equals(DbType.postgresql)){
            return StringUtils.equalsIgnoreCase(dataType, that.dataType) &&
                    StringUtils.equalsIgnoreCase(length, that.length) &&
                    Objects.equal(decimalLength, that.decimalLength);
        }
        return false;
    }
    @Override
    public int hashCode() {
        return Objects.hashCode(autoIncrDefaultValue, autoIncrement, comment, dataType, defaultValue, fieldName, length, decimalLength, notNull, primaryKey, fieldRank, compoundType, unSigned, zeroFill, tableName);
    }

    @Override
    @JsonIgnore
    public String[] sqlCompareFields() {
        return includeFields;
    }

    /**
     * 增加获取对比字段属性和字段描述信息
     *
     * @return map
     */
    @Override
    @JsonIgnore
    public LinkedHashMap<String, String> sqlCompareFieldAndDesc() {
        return includeFieldAndDescMap;
    }
}
